#include "CCanvas.h"

BEGIN_EVENT_TABLE(CCanvas, wxScrolledWindow)
EVT_PAINT(CCanvas::OnPaint)
EVT_LEFT_UP(CCanvas::OnLeftButtonUp)
EVT_KEY_UP(CCanvas::OnKeyUp)
END_EVENT_TABLE()

CCanvas::CCanvas(wxWindow *lpParentFrame): wxScrolledWindow(lpParentFrame)
{
    //ctor
    int i;
    lpbr[0] = wxBrush(wxColor(192, 192, 192));
    lpbr[1] = wxBrush(wxColor(192, 192, 255));
    lpbr[2] = wxBrush(wxColor(255, 192, 192));
    lpbr[3] = wxBrush(wxColor(255, 192, 255));
    lpbr[4] = wxBrush(wxColor(192, 255, 192));
    lpbr[5] = wxBrush(wxColor(192, 255, 255));
    lpbr[6] = wxBrush(wxColor(255, 255, 192));
    lpbr[7] = wxBrush(wxColor(64, 64, 64));
    lpbr[8] = wxBrush(wxColor(64, 64, 255));
    lpbr[9] = wxBrush(wxColor(255, 64, 64));
    lpbr[10] = wxBrush(wxColor(255, 64, 255));
    lpbr[11] = wxBrush(wxColor(64, 255, 64));
    lpbr[12] = wxBrush(wxColor(64, 255, 255));
    lpbr[13] = wxBrush(wxColor(255, 255, 64));
    iBaseWidth = COLUMN_INTERVAL * 3;
    iColumnTop = BASE_LINE - COLUMN_LENGTH;
    iColumeDiameter = COLUMN_RADIUS * 2;
    for (i = 0; i < 7; ++i)
    {
        lpiPlateRadius[6 - i] = MAX_PLATE - i * PLATE_DIFF;
    }
    iHand = 0;
    iSelect = -1;
}

void CCanvas::OnSize(wxSizeEvent &event)
{
    ;
}

void CCanvas::OnPaint(wxPaintEvent &event)
{
    wxPaintDC dc(this);
    dc.SetBackground(*wxWHITE_BRUSH);
    dc.Clear();
    DrawBases(dc);
    DrawPlates(dc);
    DrawHand(dc);
    DrawMark(dc);
}

void CCanvas::OnLeftButtonUp(wxMouseEvent &event)
{
    wxCoord x, y;
    event.GetPosition(&x, &y);

    Refresh();
}

void CCanvas::OnKeyUp(wxKeyEvent &event)
{
    int iKeyCode, iDest, iBackUp, iCase;
    bool bMove;
    bMove = false;
    iKeyCode = event.GetKeyCode();
    switch(iKeyCode)
    {
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
        if (hanoi.m_lpiSteps[1] > 0)
        {
            wxMessageDialog MD(this, _("The records will be discarded!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
            if (MD.ShowModal() == wxID_OK)
            {
                hanoi.SetLayers(int(iKeyCode - '0'));
                iHand = 0;
            }
        }
        else
        {
            hanoi.SetLayers(int(iKeyCode - '0'));
            iHand = 0;
        }
        break;
    case WXK_NUMPAD1:
    case WXK_NUMPAD2:
    case WXK_NUMPAD3:
    case WXK_NUMPAD4:
    case WXK_NUMPAD5:
    case WXK_NUMPAD6:
    case WXK_NUMPAD7:
        if (hanoi.m_lpiSteps[1] > 0)
        {
            wxMessageDialog MD(this, _("The records will be discarded!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
            if (MD.ShowModal() == wxID_OK)
            {
                hanoi.SetLayers(int(iKeyCode - WXK_NUMPAD0));
                iHand = 0;
            }
        }
        else
        {
            hanoi.SetLayers(int(iKeyCode - WXK_NUMPAD0));
            iHand = 0;
        }
        break;
    case 'F':
    case 'f':
        iDest = hanoi.Forward();
        if (iDest != -1)
        {
            iHand = iDest;
        }
        break;
    case 'B':
    case 'b':
        iDest = hanoi.Backward();
        if (iDest != -1)
        {
            iHand = iDest;
        }
        break;
    case 'R':
    case 'r':
        if (hanoi.m_lpiSteps[1] > 0)
        {
            wxMessageDialog MD(this, _("The records will be discarded!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
            if (MD.ShowModal() == wxID_OK)
            {
                iHand = 0;
                hanoi.Init();
            }
        }
        else
        {
            iHand = 0;
            hanoi.Init();
        }
        break;
    case 'A':
    case 'a':
        if (hanoi.m_lpiSteps[1] > 0)
        {
            wxMessageDialog MD(this, _("The records will be discarded!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
            if (MD.ShowModal() == wxID_OK)
            {
                iHand = 0;
                hanoi.Init();
                hanoi.GenerateAuto();
            }
        }
        else
        {
            iHand = 0;
            hanoi.Init();
            hanoi.GenerateAuto();
        }
        break;
    case WXK_UP:
        iSelect = -1;
        break;
    case WXK_DOWN:
        iSelect = hanoi.m_lpiColumn[iHand * MAX_LAYERS + hanoi.m_lpiHeight[iHand] - 1];
        break;
    case WXK_LEFT:
        iDest = (iHand + 2) % 3;
        if (iSelect != -1)
        {
            iCase = hanoi.TestMove(iHand, iDest);
            switch (iCase)
            {
            case 0:
                hanoi.Forward();
                iHand = iDest;
                break;
            case 1:
                if (hanoi.m_lpiSteps[0] < hanoi.m_lpiSteps[1])
                {
                    wxMessageDialog MD(this, _("The previous records will be written!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
                    if (MD.ShowModal() == wxID_OK)
                    {
                        hanoi.MoveWithRecord(iHand, iDest);
                        iHand = iDest;
                        bMove = true;
                    }
                }
                else
                {
                    hanoi.MoveWithRecord(iHand, iDest);
                    iHand = iDest;
                    bMove = true;
                }
                break;
            }
            iSelect = -1;
        }
        else
        {
            if (hanoi.m_lpiHeight[iDest] > 0)
            {
                iHand = iDest;
            }
            else
            {
                iBackUp = (iHand + 1) % 3;
                if (hanoi.m_lpiHeight[iBackUp] > 0)
                {
                    iHand = iBackUp;
                }
            }
        }
        break;
    case WXK_RIGHT:
        iDest = (iHand + 1) % 3;
        if (iSelect != -1)
        {
            iCase = hanoi.TestMove(iHand, iDest);
            switch (iCase)
            {
            case 0:
                hanoi.Forward();
                iHand = iDest;
                break;
            case 1:
                if (hanoi.m_lpiSteps[0] < hanoi.m_lpiSteps[1])
                {
                    wxMessageDialog MD(this, _("The previous records will be discarded and written!\nWill you continue?"), _("Warning"), wxOK|wxCANCEL);
                    if (MD.ShowModal() == wxID_OK)
                    {
                        hanoi.MoveWithRecord(iHand, iDest);
                        iHand = iDest;
                        bMove = true;
                    }
                }
                else
                {
                    hanoi.MoveWithRecord(iHand, iDest);
                    iHand = iDest;
                    bMove = true;
                }
                break;
            }
            iSelect = -1;
        }
        else
        {
            if (hanoi.m_lpiHeight[iDest] > 0)
            {
                iHand = iDest;
            }
            else
            {
                iBackUp = (iHand + 2) % 3;
                if (hanoi.m_lpiHeight[iBackUp] > 0)
                {
                    iHand = iBackUp;
                }
            }
        }
        break;
    }
    Refresh();
    if (bMove && hanoi.m_lpiHeight[2] == hanoi.m_nLayers)
    {
        wxMessageBox(_("Congratulations! You win!"), _("Tip"));
        bMove = false;
    }
}

void CCanvas::DrawBases(wxPaintDC &dc)
{
    int i;
    dc.SetPen(*wxBLACK_PEN);
    dc.SetBrush(*wxBLACK_BRUSH);
    dc.DrawRectangle(LEFT_BASE, BASE_LINE, iBaseWidth, PLATE_THICKNESS);
    for (i = 0; i < 3; ++i)
    {
        dc.DrawRectangle(LEFT_COLUMN - COLUMN_RADIUS + i * COLUMN_INTERVAL, iColumnTop, iColumeDiameter, COLUMN_LENGTH);
    }
}

void CCanvas::DrawPlates(wxPaintDC &dc)
{
    int i, j, k;
    dc.SetPen(*wxBLACK_PEN);
    for (i = 0; i < 3; ++i)
    {
        for (j = 0; j < hanoi.m_lpiHeight[i]; ++j)
        {
            k = hanoi.m_lpiColumn[i * MAX_LAYERS + j];
            if (iSelect == k)
            {
                dc.SetBrush(lpbr[k + 7]);
            }
            else
            {
                dc.SetBrush(lpbr[k]);
            }
            dc.DrawRectangle
            (
                LEFT_COLUMN + i * COLUMN_INTERVAL - lpiPlateRadius[k],
                BASE_LINE - (j + 1) * PLATE_DISTANCE,
                lpiPlateRadius[k] * 2,
                PLATE_THICKNESS
            );
        }
    }
}

void CCanvas::DrawHand(wxPaintDC &dc)
{
    wxPoint lpwxpntVertices[7];
    lpwxpntVertices[0] = wxPoint(LEFT_COLUMN + iHand * COLUMN_INTERVAL, BASE_LINE - hanoi.m_lpiHeight[iHand] * PLATE_DISTANCE);
    lpwxpntVertices[1] = lpwxpntVertices[0] + wxPoint(ARROW_RADIUS, -ARROW_HEIGHT);
    lpwxpntVertices[2] = lpwxpntVertices[0] + wxPoint(HANDLE_RADIUS, -ARROW_HEIGHT);
    lpwxpntVertices[3] = lpwxpntVertices[0] + wxPoint(HANDLE_RADIUS, -HANDLE_LENGTH);
    lpwxpntVertices[4] = lpwxpntVertices[0] + wxPoint(-HANDLE_RADIUS, -HANDLE_LENGTH);
    lpwxpntVertices[5] = lpwxpntVertices[0] + wxPoint(-HANDLE_RADIUS, -ARROW_HEIGHT);
    lpwxpntVertices[6] = lpwxpntVertices[0] + wxPoint(-ARROW_RADIUS, -ARROW_HEIGHT);
    dc.SetPen(*wxBLACK_PEN);
    dc.SetBrush(*wxWHITE_BRUSH);
    dc.DrawPolygon(7, lpwxpntVertices);
}

void CCanvas::DrawMark(wxPaintDC &dc)
{
    wxCoord width, height;
    dc.SetFont(wxFont(MARK_SIZE, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
    wxString wxstrMark;
    wxstrMark = _("A");
    dc.GetTextExtent(wxstrMark, &width, &height);
    dc.DrawText(wxstrMark, LEFT_COLUMN - width / 2, MARK_TOP);
    wxstrMark = _("B");
    dc.GetTextExtent(wxstrMark, &width, &height);
    dc.DrawText(wxstrMark, LEFT_COLUMN + COLUMN_INTERVAL - width / 2, MARK_TOP);
    wxstrMark = _("C");
    dc.GetTextExtent(wxstrMark, &width, &height);
    dc.DrawText(wxstrMark, LEFT_COLUMN + COLUMN_INTERVAL * 2 - width / 2, MARK_TOP);
    wxstrMark = wxString::Format(_("Steps: %d/%d"), hanoi.m_lpiSteps[0], hanoi.m_lpiSteps[1]);
    dc.GetTextExtent(wxstrMark, &width, &height);
    dc.DrawText(wxstrMark, STEP_MID - width / 2, STEP_TOP);
}
